from jsAst.types import *
from tokenDef import *
from Parser import Parser

G={
    'log':lambda x:print(x),
    '__p':None,
}

class Runner:
    def __init__(self):
        self.map={
            '+':self._add,
            '-':self._sub,
            '*':self._mul,
            '/':self._div,
            '=':self._assign,
            '(':self._pt,
        }
    def work(self,scope,ast):
        for x in ast['cmds']:
            if not isinstance(x,AstNode):
                print('warning: useless cmd '+x)
            else:
                self.execAst(x,scope)

    def execAst(self,ast:AstNode,scope):
        if ast.op in self.map:
            return self.map[ast.op](ast,scope)
        else:
            raise RuntimeError(ast.line,f'unknown opration {ast.op}')

    def _assign(self,ast:AstNode,scope):
        value=self._getValue(ast.right,scope)
        if isinstance(ast.left,jVar):
            scope[ast.left.value]=value
        else:#TODO 处理 a[0]=1
            pass
        return value

    def _add(self,ast:AstNode,scope):
        left=self._getValue(ast.left,scope)
        right=self._getValue(ast.right,scope)
        if hasattr(left,'add'):
            try:
                return left.add(right)
            except Exception as e:
                raise RuntimeError(ast.line,str(e))
        else:
            raise RuntimeError(ast.line,f'{type(left)} dont have add method')

    def _sub(self,ast:AstNode,scope):
        if ast.left is None:
            left=jInt(0)
        else:
            left=self._getValue(ast.left,scope)
        right=self._getValue(ast.right,scope)
        if hasattr(left,'sub'):
            try:
                return left.sub(right)
            except Exception as e:
                raise RuntimeError(ast.line,str(e))
        else:
            raise RuntimeError(ast.line,f'{type(left)} dont have sub method')

    def _mul(self,ast:AstNode,scope):
        left=self._getValue(ast.left,scope)
        right=self._getValue(ast.right,scope)
        if hasattr(left,'mul'):
            try:
                return left.mul(right)
            except Exception as e:
                raise RuntimeError(ast.line,str(e))
        else:
            raise RuntimeError(ast.line,f'{type(left)} dont have mul method')

    def _div(self,ast:AstNode,scope):
        left=self._getValue(ast.left,scope)
        right=self._getValue(ast.right,scope)
        if hasattr(left,'div'):
            try:
                return left.div(right)
            except Exception as e:
                raise RuntimeError(ast.line,str(e))
        else:
            raise RuntimeError(ast.line,f'{type(left)} dont have div method')
    
    def _pt(self,ast:AstNode,scope):
        if ast.left is None:
            return self._getValue(ast.right,scope)
        else:#执行函数
            left=self._getValue(ast.left,scope)
            if isinstance(ast.left,jVar):
                if ast.left.value in scope:
                    fun=scope[ast.left.value]
                    params=[]
                    for x in ast.right:
                        params.append(self._getValue(x,scope))
                    if callable(fun):
                        fun(*params)
                    else:#调用代码里面定义的函数
                        pass
            else:
                raise RuntimeError(ast.line,f'unknown caller {ast.left}')
    
    def _getValue(self,ast,scope):
        if isinstance(ast,AstNode):
            return self.execAst(ast,scope)
        elif isinstance(ast,jVar):
            if ast.value not in scope:
                raise RuntimeError(ast.line,'undefined value:'+ast.value)
            else:
                return scope[ast.value]
        else:
            return ast

class RuntimeError(Exception):
    def __init__(self,line,msg) -> None:
        self.line=line
        self.msg=msg
    def __str__(self) -> str:
        return f'Runtime error at line {self.line}:{self.msg}'

if __name__ == '__main__':
    a=Parser('test.js')
    res=a.genAst()
    Runner().work(G,res)